#include <vector>
#include <glibmm-2.4/glibmm/object.h>
#include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/split.hpp>

#include "gstreamerengine.h"

GstreamerEngine::GstreamerEngine() {
    static bool initialized = false;
    if (!initialized) {
        Gst::init();
        initialized = true;
        cout << "GstreamerEngine" << endl;
    }
    m_sURL = "";
}

GstreamerEngine::GstreamerEngine(string URL) {
    GstreamerEngine();
    SetURL(URL);
}

GstreamerEngine::~GstreamerEngine() {
    m_playbin->set_state(Gst::STATE_NULL);
}

void GstreamerEngine::Init() {
    if (m_sURL == "")
        throw exception();

    string temp_string;
    vector< string > temp_string_vector;
    boost::algorithm::split(temp_string_vector,
            m_sURL, boost::algorithm::is_any_of("://"));
    if (temp_string_vector[0] == "v4l") {
        temp_string = "v4lsrc device=/dev/" + temp_string_vector[3] + " ! ffmpegcolorspace ! videoscale name=vs";
    } else if (temp_string_vector[0] == "http") {
        temp_string = "souphttpsrc location=" + m_sURL + " ! multipartdemux ! jpegdec ! ffmpegcolorspace ! videoscale name=vs";
    } else if (temp_string_vector[0] == "test") {
        //exception
        temp_string = "videotestsrc ! xvimagesink name=vsink";
    }

    Glib::RefPtr<Gst::Element> temp_el = Gst::Parse::launch(temp_string);
    m_playbin = Glib::RefPtr<Gst::Pipeline>::cast_dynamic(temp_el);
    //didn't find another way to get last element from the pipeline
    m_lastel =  m_playbin->get_element("vs");
}

void GstreamerEngine::LinkToX11Window(Window window) {
    m_vsink = Gst::ElementFactory::create_element("xvimagesink");

    m_playbin->add(m_vsink);
    m_lastel->link(m_vsink);

    Glib::RefPtr<Gst::Pad> pad = m_vsink->get_pad("sink");

    pad->add_buffer_probe(sigc::mem_fun(*this,
            &GstreamerEngine::on_video_pad_got_buffer));
    m_window = window;
    Glib::RefPtr<Gst::Bus> bus = m_playbin->get_bus();
    bus->enable_sync_message_emission();
    bus->signal_sync_message().connect(
            sigc::mem_fun(*this, &GstreamerEngine::on_bus_message_sync));
}

void GstreamerEngine::LinkToClutter() {

}

bool GstreamerEngine::on_video_pad_got_buffer(const Glib::RefPtr<Gst::Pad>& pad,
        const Glib::RefPtr<Gst::MiniObject>& data) {

    Glib::RefPtr<Gst::Buffer> buffer =
            Glib::RefPtr<Gst::Buffer>::cast_dynamic(data);
    if (buffer) {
        Glib::RefPtr<Gst::Caps> caps = buffer->get_caps();

        const Gst::Structure structure = caps->get_structure(0);
        if (structure) {
            structure.get_field("width", m_width);
            structure.get_field("height", m_height);
        }

    }
    return true;
}

void GstreamerEngine::on_bus_message_sync(
        const Glib::RefPtr<Gst::Message>& message) {
    if (message->get_message_type() != Gst::MESSAGE_ELEMENT)
        return;

    if (!message->get_structure().has_name("prepare-xwindow-id"))
        return;

    Glib::RefPtr<Gst::Element> element =
            Glib::RefPtr<Gst::Element>::cast_dynamic(message->get_source());

    Glib::RefPtr< Gst::ElementInterfaced<GstBase::XOverlay> > xoverlay =
            Gst::Interface::cast <GstBase::XOverlay > (element);

    if (xoverlay) {
        xoverlay->set_xwindow_id(m_window);
    }
}

bool GstreamerEngine::GetSize(int *width, int *height) {
    if (m_width && m_height) {
        *width = m_width;
        *height = m_height;
        return true;
    }
    return false;
}

void GstreamerEngine::SetURL(string URL) {
    m_sURL = URL;
}

void GstreamerEngine::Stop() {

}

void GstreamerEngine::Play() {
    m_playbin->set_state(Gst::STATE_PLAYING);
}


